package com.template.utils.redis;

import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.time.Instant;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Component
@RequiredArgsConstructor
public class KeyOps {

    final RedisTemplate<String, Object> redisTemplate;


    /**
     * <h4> Key </h4>
     * 获取所有的 key
     * <li> keys * </li>
     */
    public Set<String> keys() {
        return keys("*");
    }

    /**
     * <h4> Key </h4>
     * 获取匹配所有的 key
     * <li> keys _pattern </li>
     */
    public Set<String> keys(String pattern) {
        return redisTemplate.keys(pattern);
    }

    /**
     * <h4> Key </h4>
     * 判断 key 是否存在
     * <li> exists _key </li>
     */
    public Boolean exists(String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * <h4> Key </h4>
     * 修改 key
     * <li> rename _oldKey _newKey </li>
     */
    public void rename(String oldKey, String newKey) {
        redisTemplate.rename(oldKey, newKey);
    }

    /**
     * <h4> Key </h4>
     * 若 oldKey 存在则修改 key
     * <li> renamenx _oldKey _newKey </li>
     */
    public Boolean renameNx(String oldKey, String newKey) {
        return redisTemplate.renameIfAbsent(oldKey, newKey);
    }

    /**
     * <h4> Key </h4>
     * 随机获取一个 key
     * <li> randomkey </li>
     */
    public String randomKey() {
        return redisTemplate.randomKey();
    }

    /**
     * <h4> Key </h4>
     * 获得 key 的类型
     * <li> type _key </li>
     */
    public DataType type(String key) {
        return redisTemplate.type(key);
    }

    /**
     * <h4> Key </h4>
     * 删除指定 keys 的缓存
     * <li> del _key1 _key2 ... </li>
     */
    public Long del(String... keys) {
        if (keys == null || keys.length == 0) return 0L;
        if (keys.length == 1) {
            redisTemplate.delete(keys[0]);
            return 1L;
        }
        return redisTemplate.delete(Arrays.asList(keys));
    }

    /**
     * <h4> Key </h4>
     * 删除指定 keys 的缓存
     * <li> del _key1 _key2 ... </li>
     */
    public Long del(Collection<String> keys) {
        return redisTemplate.delete(keys);
    }

    /**
     * <h4> Key </h4>
     * 非阻塞删除，仅将 keys 从 keyspace 元数据中删除，真正的删除会在后续异步中操作
     * <li> unlink _key1 _key2 ... </li>
     */
    public Long unlink(String... keys) {
        if (keys == null || keys.length == 0) return 0L;
        if (keys.length == 1) {
            redisTemplate.unlink(keys[0]);
            return 1L;
        }
        return redisTemplate.unlink(Arrays.asList(keys));
    }

    /**
     * <h4> Key </h4>
     * 非阻塞删除，仅将 keys 从 keyspace 元数据中删除，真正的删除会在后续异步中操作
     * <li> unlink _key1 _key2 ... </li>
     */
    public Long unlink(Collection<String> keys) {
        return redisTemplate.unlink(keys);
    }

    /**
     * <h4> Key </h4>
     * 获取 key 的过期时间
     * <li> ttl _key </li>
     *
     * @return -1 表示永久 -2 表示已过期
     */
    public Long ttl(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * <h4> Key </h4>
     * 获取 key 的过期时间（毫秒）
     * <li> pttl _key </li>
     *
     * @return -1 表示永久 -2 表示已过期
     */
    public Long pTtl(String key) {
        return redisTemplate.getExpire(key, TimeUnit.MILLISECONDS);
    }

    /**
     * <h4> Key </h4>
     * 获取 key 的过期时间
     * <li> pttl _key </li>
     *
     * @return -1 表示永久 -2 表示已过期
     */
    public Long ttl(String key, long expire, TimeUnit timeUnit) {
        return redisTemplate.getExpire(key, timeUnit);
    }

    /**
     * <h4> Key </h4>
     * 指定过期时间
     * <li> expire _key _expire </li>
     */
    public Boolean expire(String key, long seconds) {
        return redisTemplate.expire(key, seconds, TimeUnit.SECONDS);
    }

    /**
     * <h4> Key </h4>
     * 指定过期时间，到指定时间戳过期
     * <li> expireat _key _timestamp </li>
     */
    public Boolean expireAt(String key, Date date) {
        return redisTemplate.expireAt(key, date);
    }

    /**
     * <h4> Key </h4>
     * 为指定 key 设定过期时间，到指定 date 时过期
     * <li> expireat _key _timestamp </li>
     */
    public Boolean expireAt(String key, Instant instant) {
        return redisTemplate.expireAt(key, instant);
    }

    /**
     * <h4> Key </h4>
     * 去除指定 key 的过期时间
     * <li> persist _key </li>
     */
    public Boolean persist(String key) {
        return redisTemplate.persist(key);
    }

    /**
     * <h4> Key </h4>
     * 将当前数据库的 key 移动到指定数据库中
     * <li> move _key _dbIndex </li>
     */
    public Boolean move(String key, int dbIndex) {
        return redisTemplate.move(key, dbIndex);
    }

    /**
     * <h4> Key </h4>
     * 迭代 key
     * <li> move _key _dbIndex </li>
     */
    public Cursor<String> scan(ScanOptions options) {
        return redisTemplate.scan(options);
    }

    // flushdb \\ flushall


    @SuppressWarnings("unchecked")
    public byte[] rawKey(String key) {
        Assert.notNull(key, "non null key required");
        RedisSerializer keySerializer = redisTemplate.getKeySerializer();
        return keySerializer.serialize(key);
    }

    @SuppressWarnings("unchecked")
    public byte[] rawValue(Object value) {
        Assert.notNull(value, "non null value required");
        RedisSerializer valueSerializer = redisTemplate.getValueSerializer();
        return valueSerializer.serialize(value);
    }
}
